/*
 * $Id: $
 */
package org.rcgwt.client.ui.grid.provider;

import org.rcgwt.client.ui.grid.event.OrderChangedEvent;
import org.rcgwt.client.ui.grid.event.OrderChangedHandler;
import org.rcgwt.client.ui.grid.internal.HandlerManagerProvider;

import com.google.gwt.event.shared.HandlerRegistration;

/**
 * 
 * @author Olivier Oeuillot (latest modification by $Author: $)
 * @version $Revision: $ $Date: $
 */
public class BasicOrderProvider extends HandlerManagerProvider implements
        IOrderProvider {

    private static final IOrderState[] EMPTY_ARRAY = new IOrderState[0];

    private static final boolean[] TRUE_BOOLEAN_ARRAY = new boolean[] { true };

    private static final boolean[] BOOLEAN_EMPTY_ARRAY = new boolean[0];

    private IOrderState orderStates[] = EMPTY_ARRAY;

    @Override
    public void appendOrder(int modelIndex, boolean ascending) {
        if (orderStates.length == 0) {
            setOrder(modelIndex, ascending);
            return;
        }

        IOrderState newOrderState = new OrderState(modelIndex, ascending);

        IOrderState newOrderStates[] = new IOrderState[orderStates.length + 1];
        System.arraycopy(orderStates, 0, newOrderStates, 0, orderStates.length);

        newOrderStates[orderStates.length] = newOrderState;

        boolean deltas[] = new boolean[orderStates.length + 1];
        deltas[orderStates.length] = true;

        processNewOrderState(newOrderStates, deltas);
    }

    @Override
    public void changeOrderDirection(int modelIndex) {

        int idx = -1;
        for (int i = 0; i < orderStates.length; i++) {
            if (orderStates[i].getModelIndex() == modelIndex) {
                idx = i;
                break;
            }
        }

        if (idx < 0) {
            return;
        }

        boolean newState[] = new boolean[orderStates.length];
        newState[idx] = true;

        IOrderState newOrderStates[] = new IOrderState[orderStates.length];
        System.arraycopy(orderStates, 0, newOrderStates, 0, orderStates.length);

        newOrderStates[idx] = new OrderState(modelIndex, !orderStates[idx]
                .isAsceding());

        processNewOrderState(newOrderStates, newState);
    }

    @Override
    public void clearOrder() {
        processNewOrderState(EMPTY_ARRAY, BOOLEAN_EMPTY_ARRAY);
    }

    @Override
    public IOrderState[] getOrderStates() {
        return orderStates;
    }

    @Override
    public void setOrder(int modelIndex, boolean ascending) {

        IOrderState newOrderState[] = new IOrderState[] { new OrderState(
                modelIndex, ascending) };

        processNewOrderState(newOrderState, TRUE_BOOLEAN_ARRAY);
    }

    private void processNewOrderState(IOrderState[] newOrderState,
            boolean changedMask[]) {

        IOrderState old[] = orderStates;
        orderStates = newOrderState;

        OrderChangedEvent.fire(this, old, newOrderState, changedMask);
    }

    @Override
    public HandlerRegistration addOrderChangedHandler(
            OrderChangedHandler<IOrderState[]> handler) {
        return addHandler(handler, OrderChangedEvent.getType());
    }

    @Override
    public IOrderState getOrderState(int modelIndex) {
        for (IOrderState orderState : orderStates) {
            if (orderState.getModelIndex() == modelIndex) {
                return orderState;
            }
        }

        return null;
    }

    protected static class OrderState implements IOrderState {

        private int modelIndex;

        private boolean ascending;

        public OrderState(int modelIndex, boolean ascending) {
            this.modelIndex = modelIndex;
            this.ascending = ascending;
        }

        @Override
        public int getModelIndex() {
            return modelIndex;
        }

        @Override
        public boolean isAsceding() {
            return ascending;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + (ascending ? 1231 : 1237);
            result = prime * result + modelIndex;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (!(obj instanceof OrderState)) {
                return false;
            }
            OrderState other = (OrderState) obj;
            if (ascending != other.ascending) {
                return false;
            }
            if (modelIndex != other.modelIndex) {
                return false;
            }
            return true;
        }

    }
}